package com.bjx.flow.service.impl;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.bjx.api.flow.domain.vo.FlowNodeVO;
import com.bjx.api.flow.domain.vo.FlowStartVO;
import com.bjx.api.flow.domain.vo.NextNodeVO;
import com.bjx.api.system.domain.query.UserQuery;
import com.bjx.api.system.domain.vo.UserVO;
import com.bjx.api.system.service.RemoteUserService;
import com.bjx.common.core.domain.R;
import com.bjx.common.core.exception.BJXException;
import com.bjx.common.core.util.SpringUtil;
import com.bjx.common.security.domain.LoginUserVO;
import com.bjx.common.security.util.LoginUtil;
import com.bjx.flow.approve.user.ApprovalUserDispose;
import com.bjx.flow.domain.entity.FlowInstanceEntity;
import com.bjx.flow.domain.query.FlowInstanceQuery;
import com.bjx.flow.domain.query.FlowNodeQuery;
import com.bjx.flow.domain.vo.FlowInstanceVO;
import com.bjx.flow.mmx.bean.MmxNextNodeData;
import com.bjx.flow.mmx.bean.MmxNodeData;
import com.bjx.flow.mmx.constant.MmxNodeConstant;
import com.bjx.flow.mmx.domain.entity.MmxFlowDefinitionEntity;
import com.bjx.flow.mmx.service.MmxFlowDataService;
import com.bjx.flow.mmx.service.MmxFlowDefinitionService;
import com.bjx.flow.mmx.service.MmxRuntimeService;
import com.bjx.flow.mmx.task.MmxAbstractTaskHandler;
import com.bjx.flow.service.IBaseFlowService;
import com.bjx.flow.service.IFlowInstanceService;
import com.bjx.flow.util.TaskNodeUtil;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.delegate.Expression;
import org.camunda.bpm.engine.impl.RepositoryServiceImpl;
import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity;
import org.camunda.bpm.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.camunda.bpm.engine.impl.pvm.process.ActivityImpl;
import org.camunda.bpm.engine.impl.task.TaskDefinition;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.ExtensionElements;
import org.camunda.bpm.model.bpmn.instance.Task;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * @desc: 基础流程服务实现
 * @author: liushuai
 * @date: 2022/11/1 16:20
 */
@Slf4j
@Service
public class BaseFlowServiceImpl implements IBaseFlowService {
    @Resource
    private RuntimeService runtimeService;

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RemoteUserService remoteUserService;
    @Resource
    private MmxRuntimeService mmxRuntimeService;

    @Resource
    private MmxFlowDefinitionService mmxFlowDefinitionService;
    @Resource
    private MmxFlowDataService mmxFlowDataService;

    @Resource
    private ApprovalUserDispose relationDisposeHandler;

    @Resource(name = "bjxFlowInstanceServiceImpl")
    private IFlowInstanceService flowInstanceService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ProcessInstance startFlow(FlowStartVO flowStartVO) {
        Map<String, Object> map = Optional.ofNullable(flowStartVO.getVariables()).orElse(new HashMap<>());
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(flowStartVO.getDefinitionKey(), flowStartVO.getBusinessId(), flowStartVO.getBusinessId(), map);
        LoginUserVO loginUserVO = LoginUtil.loginInfo();
        flowInstanceService.insertFlowInstance(FlowInstanceEntity.builder()
                .businessId(flowStartVO.getBusinessId())
                .name(flowStartVO.getName())
                .instanceId(processInstance.getProcessInstanceId())
                .initiator(Long.valueOf(loginUserVO.getUserId()))
                .comId(Long.valueOf(loginUserVO.getComId()))
                .orgId(Long.valueOf(loginUserVO.getOrgId()))
                .businessSign(flowStartVO.getBusinessSign())
                .build());
        return processInstance;
    }

    @Override
    public FlowNodeVO selectFirstFlowNodeInfoByDefinitionId(String definitionId) {
        return null;
    }

    @Override
    public IPage<FlowInstanceVO> listTodoTaskByCurrentUser(FlowInstanceQuery flowInstanceQuery) {
        String userId = LoginUtil.loginInfo().getUserId();
        flowInstanceQuery.setAssignee(userId);
        IPage<FlowInstanceVO> flowInstanceVOIPage = flowInstanceService.listFlowInstancePage(flowInstanceQuery);
        return flowInstanceVOIPage;
    }

    @Override
    public List<FlowNodeVO> getNextTaskNode(FlowNodeQuery flowNodeQuery) {
        if (StrUtil.isEmpty(flowNodeQuery.getInstanceId())) {
            throw new BJXException("流程实例id不能为空");
        }
        List<FlowNodeVO> lists = new ArrayList<>();
        Map<String, Object> condition = runtimeService.getVariables(flowNodeQuery.getInstanceId());
        if (CollectionUtil.isNotEmpty(flowNodeQuery.getParams())) {
            condition.putAll(flowNodeQuery.getParams());
        }
        //获取下一个审批节点信息
        List<TaskDefinition> nextTaskInfos = getNextTaskInfos(flowNodeQuery.getInstanceId(), condition);
        if (nextTaskInfos.size() == 1) {
            TaskDefinition taskDefinition = nextTaskInfos.get(0);
            Expression nameExpression = taskDefinition.getNameExpression();
            if ("结束".equals(nameExpression.getExpressionText())) {
                lists.add(FlowNodeVO.builder()
                        .id(taskDefinition.getKey())
                        .name(nameExpression.getExpressionText())
                        .isEnd(true)
                        .build());
                return lists;
            }
        }
        List<String> keys = nextTaskInfos.stream().map(TaskDefinition::getKey).collect(Collectors.toList());
        //获取流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(flowNodeQuery.getInstanceId())
                .singleResult();
        BpmnModelInstance bpmnModelInstance = repositoryService.getBpmnModelInstance(processInstance.getProcessDefinitionId());
        Collection<Task> processCollection = bpmnModelInstance.getModelElementsByType(Task.class);

        FlowInstanceEntity flowInstanceEntity = flowInstanceService.selectFlowInstanceByInstanceId(flowNodeQuery.getInstanceId());
        if (CollectionUtil.isNotEmpty(processCollection)) {
            for (Task process : processCollection) {
                if (!keys.contains(process.getId())) {
                    continue;
                }
                ExtensionElements extensionElements = process.getExtensionElements();
                FlowNodeVO flowNodeVO = FlowNodeVO.builder()
                        .name(process.getName())
                        .id(process.getId())
                        .isEnd(false)
                        .build();
                if (ObjectUtil.isNull(extensionElements)) {
                    continue;
                }
                List<ModelElementInstance> properties = extensionElements.getElementsQuery().list();
                for (ModelElementInstance propertie : properties) {
                    String name = propertie.getAttributeValue("name");
                    if ("execute".equals(name)) {
                        String value = propertie.getAttributeValue("value");
                        JSONObject jsonObject = new JSONObject(value);
                        jsonObject.set("initiatorOrg", flowInstanceEntity.getOrgId());
                        jsonObject.set("initiatorCompany", flowInstanceEntity.getComId());
                        List<UserVO> users = new ArrayList<>();
                        relationDisposeHandler.dispose(jsonObject, users);
                        users = users.stream().distinct().collect(Collectors.toList());
                        flowNodeVO.setUsers(users);
                    }
                }
                lists.add(flowNodeVO);
            }

        }
        return lists;

    }

    @Override
    public FlowNodeVO selectFirstFlowNodeInfoByDefinitionKey(String definitionKey) {
        MmxFlowDefinitionEntity mmxFlowDefinitionEntity = mmxFlowDefinitionService.selectFlowDefinitionByDefinitionKey(definitionKey);
        Map<String,Object> params = new HashMap<>();
        LoginUserVO loginUserVO = LoginUtil.loginInfo();
        params.put("initiatorOrg",loginUserVO.getOrgId());
        params.put("initiatorCompany",loginUserVO.getComId());
        MmxNextNodeData mmxNextNodeData = mmxFlowDataService.taskFlow(mmxFlowDefinitionEntity.getFlowData(), null,params);
        List<MmxNodeData> nodeDataList = mmxNextNodeData.getNodeDataList();
        MmxAbstractTaskHandler mmxAbstractTaskHandler = SpringUtil.getBean(mmxNextNodeData.getFlowNodeType() + MmxNodeConstant.TASK_FLAG);
        List<MmxNodeData> mmxNodeData = mmxAbstractTaskHandler.simulationExecute(mmxNextNodeData);
        List<NextNodeVO> nodes = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(nodeDataList)) {
            for (MmxNodeData node : mmxNodeData) {
                List<String> assignees = node.getAssignees();
                List<Long> ids = assignees.stream().map(Long::valueOf).collect(Collectors.toList());
                R<List<UserVO>> r = remoteUserService.listUserVOByIds(UserQuery.builder()
                        .userIds(ids)
                        .build());
                if(r.getCode() == R.FAIL){
                    throw new BJXException("用户查询失败");
                }

                nodes.add(NextNodeVO.builder()
                        .nodeId(node.getProperties().getId())
                        .nodeName(node.getProperties().getName())
                        .users(r.getData())
                        .build());
            }
        }
        String nodeId = Optional.ofNullable(mmxNextNodeData.getCurrentNode().getProperties().getId()).orElse(mmxNextNodeData.getCurrentNode().getId());
        FlowNodeVO flowNodeVO = FlowNodeVO.builder()
                .name(mmxNextNodeData.getCurrentNode().getProperties().getName())
                .id(nodeId)
                .nextNodes(nodes)
                .build();
        return flowNodeVO;
    }

    /**
     * 获取下一个节点任务信息
     *
     * @param processInstanceId 流程实例ID
     * @return 下一个节点信息
     * @throws Exception
     */
    public List<TaskDefinition> getNextTaskInfos(String processInstanceId, Map<String, Object> condition) {
        ProcessDefinitionEntity processDefinitionEntity = null;
        String id = null;
        List<TaskDefinition> tasks = new CopyOnWriteArrayList<TaskDefinition>();
        // 获取流程发布Id信息
        String definitionId = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId)
                .singleResult().getProcessDefinitionId();
        processDefinitionEntity = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                .getDeployedProcessDefinition(definitionId);
        ExecutionEntity execution = (ExecutionEntity) runtimeService.createExecutionQuery()
                .executionId(processInstanceId).singleResult();
        // 当前流程节点Id信息
        String activitiId = execution.getActivityId();
        // 获取流程所有节点信息
        List<ActivityImpl> activitiList = processDefinitionEntity.getActivities();
        // 遍历所有节点信息
        for (ActivityImpl activityImpl : activitiList) {
            id = activityImpl.getId();
            log.debug("id:{},节点类型:{}", id, activityImpl.getProperty("type"));
            if (activitiId.equals(id)) {
                // 获取下一个节点信息
                tasks = TaskNodeUtil.nextTaskDefinitions(activityImpl, activityImpl.getId(), processInstanceId, condition);
                break;
            }
        }
        return tasks;
    }


}
